<?php

/**
 * Project:     Axe: the PHP compiling template engine
 * File:        Axe.class.php
 * Git:         https://git.oschina.net/koodo/Axe.git
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * For questions, help, comments, discussion, etc., please join the
 * Smarty mailing list. Send a blank e-mail to
 * smarty-discussion-subscribe@googlegroups.com
 *
 * @link http://www.iwshop.cn/
 * @copyright 2014 ycchen
 * @author Ycchen <koodo@qq.com>
 * @version 1.0.0
 */
class AxeCache {

    public $axe;
    public $cacheFile;
    private $cacheDir;

    public function __construct() {
        ;
    }

    /**
     * 直接输出缓存内容
     */
    public function echoCache() {
        echo file_get_contents($this->cacheFile);
    }

    /**
     * 检查是否已经缓存
     * @param type $tpl
     * @param type $cacheId
     * @return boolean
     */
    public function isCached($tpl, $cacheId) {
        // dir path
        $this->cacheDir = $this->axe->real_path
                . $this->axe->config['cache_dir']
                . DIRECTORY_SEPARATOR;
        // file path
        $this->cacheFile = $this->cacheDir
                . $this->get_cache_hash($tpl, filemtime($tpl), $cacheId)
                . '.axe^cached.php';

        if (is_file($this->cacheFile)) {
            // 缓存文件存在
            if (time() - filemtime($this->cacheFile) < $this->axe->config['cache_lifetime']) {
                // 缓存未过期
                return true;
            } else {
                // 缓存过期，同时删除文件
                $this->wipeCacheFile();
                return false;
            }
        }
    }

    /**
     * 开始ob缓存
     */
    public function start() {
        ob_start();
    }

    /**
     * 结束ob缓存并保存
     */
    public function end() {
        $c = ob_get_contents();
        $this->writeCache($c);
    }

    /**
     * 删除缓存文件
     */
    public function wipeCacheFile() {
        unlink($this->cacheFile);
    }

    /**
     * 删除所有缓存文件
     */
    public function wipeAllCache() {
        if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN') {
            exec("rmdir /s/q " . $this->cacheDir);
        } else {
            exec("rm -Rf " . $this->cacheDir);
        }
    }

    /**
     * 写入缓存文件
     * @param type $c
     */
    public function writeCache($c) {
        if (!is_dir($this->cacheDir)) {
            mkdir($this->cacheDir, 644);
        }
        file_put_contents($this->cacheFile, $this->html_compress($c));
    }

    /**
     * 生成模板唯一标签 使用md4摘要性能比较好
     * @param type $tpl
     * @param type $cacheId
     * @see http://www.cnblogs.com/AloneSword/p/3464330.html
     * @return type
     */
    private function get_cache_hash($tpl, $filemtime, $cacheId = false) {
        return hash('md4', $tpl . $filemtime . $cacheId);
    }

    /**
     * html_compress
     * @param type $html
     * @see http://www.oschina.net/code/snippet_95848_11048
     * @return type
     */
    private function html_compress($html) {
        $segments = preg_split("/(<[^>]+?>)/si", $html, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
        $compressed = array();
        $stack = array();
        $tag = '';
        foreach ($segments as $seg) {
            if (trim($seg) === '') {
                continue;
            }
            //<.../>
            if (preg_match("!<([a-z0-9]+)[^>]*?/>!si", $seg, $match)) {
                //$tag = self::format_tag($match[1]);
                $this->format_tag($match[1]);
                unset($match);
                $compressed[] = $seg;
            } else if (preg_match("!</([a-z0-9]+)[^>]*?>!si", $seg, $match)) {//</..>
                $tag = $this->format_tag($match[1]);
                if (count($stack) > 0 && $stack[count($stack) - 1] == $tag) {
                    array_pop($stack);
                    $compressed[] = $seg;
                }
                unset($match);
                //这里再最好加一段判断，可以用于修复错误的html
                //...
            } else if (preg_match("!<([a-z0-9]+)[^>]*?>!si", $seg, $match)) {//<>
                $tag = $this->format_tag($match[1]);
                //半闭合标签不需要入栈，如<br/>,<img/>
                if (!in_array($tag, array('meta', 'input', 'link', 'img', 'br'))) {
                    array_push($stack, $tag);
                }
                unset($match);
                $compressed[] = $seg;
            } else if (preg_match("~<![^>]*>~", $seg)) {
                //文档声明和注释，注释也不能删除，如<!--ie条件-->
                $compressed[] = $seg;
            } else {
                $compressed[] = in_array($tag, array('pre', 'code', 'script', 'style')) ? $seg : preg_replace('!\s!', '', $seg);
            }
        }
        unset($segments, $tag, $stack);
        return join('', $compressed);
    }

    /**
     * format_tag
     * @param type $tag
     * @return type
     */
    private function format_tag($tag) {
        return trim(strtolower($tag));
    }

}
